package edu.cmu.ece.ds.droidchat.model;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;


/**
 * generate all the attributes that will be recorded
 * by server and user after login
 * 
 * server side keeps:
 *  login session id, public key, username
 *  (see Login_Session_Attr.java)
 * 
 * user side keeps:
 *  login session id, key pair
 */
public class LoginSessionAttribute {
    private int login_session_id;
    private RSAPublicKeySpec publicKey;
    private RSAPrivateKeySpec privateKey;
    private String username;
    private int chatsessionnum=0;
    private static int KEYSIZE = 1024;
    
    /**
     * constructor for LoginSessionAttribute
     *  
     *  note that the user's public key and private key are 
     *   generated here
     */
    public LoginSessionAttribute(int login_session_id,String username){
        this.login_session_id=login_session_id;
        this.username=username;
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            KeyFactory fact = KeyFactory.getInstance("RSA");
            kpg.initialize(KEYSIZE);
            KeyPair kp=kpg.generateKeyPair();
            publicKey=fact.getKeySpec(kp.getPublic(),RSAPublicKeySpec.class);
            privateKey=fact.getKeySpec(kp.getPrivate(),RSAPrivateKeySpec.class);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
    }
    
    public int getLogin_session_id() {
        return login_session_id;
    }
    
    public RSAPublicKeySpec getPublicKey() {
        return publicKey;
    }
    
    public RSAPrivateKeySpec getPrivateKey() {
        return privateKey;
    }

    public boolean isLoggedIn(){
        if(login_session_id!=0)
            return true;
        else return false;
    }
    
	public String getUsername() {
		return username;
	}
	
	public int getChatsessionnum() {
		return ++chatsessionnum;
	}

	/*
	 * using some other user's public key ('modulus' and 'exponent')
	 *  encrypt 'sendmes'
	 *  
	 * the user should decrypt 'sendmes' with its own private key 
	 */
	public BigInteger encryptothers(Object sendmes, BigInteger modulus, BigInteger exponent)
	{
		System.out.println("encryptothers()");
		System.out.println("his modulus=" + modulus);
		System.out.println("his exponent=" + exponent);
		try {
			BigInteger encrint;
			ByteArrayOutputStream bo = new ByteArrayOutputStream();
			ObjectOutputStream oo = new ObjectOutputStream(bo);
	        oo.writeObject(sendmes);
			byte[] plaintext = bo.toByteArray();
			BigInteger plainint = new BigInteger(1,plaintext);
			encrint = plainint.modPow(exponent,modulus);
			System.out.println("plaintext has " + plaintext.length + " bytes");
			System.out.println(plainint);
			
			System.out.println("ciphertext is " + encrint);
			return encrint;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/* 
	 * using my own private key, encrypt 'sendmes'
	 */
	public BigInteger encrypt(Object sendmes)
	{
		try {
			BigInteger encrint;
			ByteArrayOutputStream bo = new ByteArrayOutputStream();
			ObjectOutputStream oo = new ObjectOutputStream(bo);
	        oo.writeObject(sendmes);
			byte[] plaintext = bo.toByteArray();
			BigInteger plainint=new BigInteger(1,plaintext);
			encrint=plainint.modPow(privateKey.getPrivateExponent(),privateKey.getModulus());
			System.out.println(plainint);
			return encrint;
//			System.out.println(encrint);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/*
	 *  using my own private key, decrypt 'encrmes'
	 *  
	 *  'ecnrmes' has to be encrypted using my public key
	 *   for me to be able to decrypt it
	 */
	public Object decrypt(BigInteger encrmes)
	{
		System.out.println("decrypt()");
		System.out.println("my public key modulus = " + publicKey.getModulus());
		System.out.println("my public key exponent = " + publicKey.getPublicExponent());
		
		System.out.println("my private key modulus = " + privateKey.getModulus());
		System.out.println("my private key exponent = " + privateKey.getPrivateExponent());
		
		System.out.println("received ciphertext is " + encrmes);
		
		try {
			BigInteger encrint = encrmes;
			BigInteger plainint = encrint.modPow(privateKey.getPrivateExponent(),privateKey.getModulus());
			
			System.out.println("plaintext is " + plainint);
			
			byte[] plaintext1 = plainint.toByteArray();
			
			System.out.println("plaintext has " + plaintext1.length + " bytes ");
			
			byte[] plaintext = plaintext1;
			
			if(plaintext1[0] == 0){
				plaintext=new byte[plaintext1.length-1];
				System.out.println("the first byte of the decrypted result is 0");
			}
			
			System.arraycopy(plaintext1, 1, plaintext, 0, plaintext.length);
	        ByteArrayInputStream bi = new ByteArrayInputStream(plaintext);
	        ObjectInputStream oi = new ObjectInputStream(bi);
	        Object receivemes = (Object) oi.readObject();
//			System.out.println((Message)receivemes);
	        return receivemes;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}
}
